
import Weapons.*;

import java.awt.Color;
import java.util.ArrayList;


import info.gridworld.actor.Actor;
import info.gridworld.grid.Grid;
import info.gridworld.grid.Location;

public abstract class Unit extends Actor{
	private Weapon wep1; //will always be the gun type
	private Weapon wep2; //will always be the cannon type
	private double cResis; //cannon resistance and gun resistance
	private double gResis;
	private int cost;
	private int moves;
	private int health;
	private Headquarters Overlord;
//	private Map terrain; //whatever map is being used for all units
	private Location move; //used for the crappy Move method (will be deleted later)
	private int player;
	
	public Unit(Gun Wep1, Cannon Wep2, double CResis, double GResis, int Cost, int Moves){
		wep1 = Wep1;
		wep2 = Wep2;
		cResis = CResis;
		gResis = GResis;
		cost = Cost;
		moves = Moves;
		health = 100;
		Overlord = new Headquarters();
		player = 999;
	}
	
	public void setPlayer(int Player){
		player = Player;
	}
	
	public int getPlayer(){
		return player;
	}
	
	public void setHQ(Headquarters newHQ){
		Overlord = newHQ;
	}
	
	public Headquarters getHQ(){
		return Overlord;
	}
	
//	public void setMap(Map Terrain){
//		terrain = Terrain;
//	}
	
	/**
	 * Takes the attack Location and determines if the unit is adjacent
	 */
	public boolean isValidAttack(Location loc){
		if (loc.equals(getLocation().getAdjacentLocation(0))){
			return true;
		}else if (loc.equals(getLocation().getAdjacentLocation(90))){
			return true;
		}else if (loc.equals(getLocation().getAdjacentLocation(180))){
			return true;
		}else if (loc.equals(getLocation().getAdjacentLocation(270))){
			return true;
		}else
			return false;
	}
	
	public ArrayList<Location> getMoveLocs(int Moves, Location loc){
		ArrayList<Location> locs = new ArrayList();
		locs.add(loc);
		//make booleans and what not to determine movement availability
		
		
		for(int counter = 0; counter < 360; counter += 90){
			boolean validmove = true;
			if(Moves - 1 /*getCost() for north location*/ >= 0){
				
				if(getGrid().get(loc.getAdjacentLocation(counter)) != (null)){
					if (((Unit) getGrid().get(loc.getAdjacentLocation(counter))).comparePlayer(999))
						validmove = false;
				}
					if(validmove){
						locs.add(loc.getAdjacentLocation(counter));
						ArrayList<Location> newlocs = getMoveLocs(Moves - 1, loc.getAdjacentLocation(counter));
						for(Location newloc: newlocs){
							for(int count = 0; count < locs.size(); count++){
								if(locs.get(count).equals(newloc)){
									locs.remove(count);
								}
							}
						}
						locs.addAll(newlocs);
					}

			}
		}
	
		return locs;
		
	}
	


	/**
	 * compares the player value
	 * @param another player value
	 * @return true if same value false in not
	 */
	public boolean comparePlayer(int Player){
		return (player == Player);
	}
	
	
	/**
	 * this takes string (n, s, w, e) and moves the unit a specific direction
	 * it will hopefully be replaced later, but for now here it is
	 */
	public void Move(String Direction){ //crappy move method (will be replaced with better one) Letters correspond to direction
		if(moves > 0){
			if(Direction.equalsIgnoreCase("N"))
				move = (getLocation().getAdjacentLocation(0));
			else if(Direction.equalsIgnoreCase("S"))
				move = (getLocation().getAdjacentLocation(180));
			else if(Direction.equalsIgnoreCase("E"))
				move = (getLocation().getAdjacentLocation(90));
			else if(Direction.equalsIgnoreCase("W"))
				move = (getLocation().getAdjacentLocation(270));
			
			if(move != null && getGrid().isValid(move)){
				moveTo(move);
				moves -= 1;
			}
			move = null; //resets for next possible move
		}
	}
	
	
	/**
	 * This decides what weapon to use and deals damage with that weapon
	 * calls retaliate method after completion
	 * @param a location to attack
	 */
	public void Attack(Location loc){
		if(isValidAttack(loc)){
			Unit a = (Unit) getGrid().get(loc);
			int oldhealth = a.getHealth();
			if(wep1 == null) //checks if wep1 even exists for this unit
				a.setHealth(a.getHealth()- getDamage(wep2, a));
			else if (wep2 == null) //checks if wep2 even exists for this unit
				a.setHealth(a.getHealth()- getDamage(wep1, a));
			else if (getDamage(wep1, a) > getDamage(wep2, a)) //if both weapons exist, checks for what is more effective
				a.setHealth(a.getHealth()- getDamage(wep1, a));
			else
				a.setHealth(a.getHealth()- getDamage(wep2, a));
			
			Overlord.addPoints(oldhealth - a.getHealth());
			
			if(a.getHealth() < 1){
				a.removeSelfFromGrid(); // kills the unit if it has 0 health
			}else{
				a.Retaliate(this); //otherwise the unit counter attacks
			}
		}else
			throw new IllegalArgumentException("Not a valid attack location");
	}
		
	/**
	 * This is the same as attack, but it does not call itself
	 * @param a unit
	 */
	public void Retaliate(Unit a){ //this is just Attack without the call to retaliate method 
		int oldhealth = a.getHealth();

		if(wep1 == null)
			a.setHealth(a.getHealth()- getDamage(wep2, a));
		else if (wep2 == null)
			a.setHealth(a.getHealth()- getDamage(wep1, a));
		else if (getDamage(wep1, a) > getDamage(wep2, a))
			a.setHealth(a.getHealth()- getDamage(wep1, a));
		else
			a.setHealth(a.getHealth()- getDamage(wep2, a));

		Overlord.addPoints(oldhealth - a.getHealth());

		
		if(a.getHealth() < 1){
			a.removeSelfFromGrid();
		}
	}
	
	/**
	 * 
	 * @return the current health (highest is 100)
	 */
	public int getHealth(){ //get the health
		return health;
	}
	
	/**
	 * sets the health (called by attack)
	 * @param new amount of health
	 */
	public void setHealth(int Health){ //set a new health
		health = Health;
	}
	
	/**
	 * cResis is the multiplier when calculating Cannon damage
	 * @return the resistance to cannon based attack (between 1 and 0)
	 */
	public double getCResis(){
		return cResis; // gives the Cannon defense modifier
	}
	
	/**
	 * cResis is the multiplier when calculating Gun damage
	 * @return the resistance to gun based attack (between 1 and 0)
	 */
	public double getGResis(){
		return gResis; //same as getEResis but for gun resistance
	}
	
	/**
	 * a small multiplier for damage (random)
	 * @return a number between .95 and 1.05
	 */
	public double getBonus(){ //finds the random bonus
		double Bonus = Math.random() * .05;
		if (Math.random() <= .5)
			return 1 + Bonus; //bonus of 1.0 - 1.05
		else
			return 1 - Bonus; //bonus of .95 - .99;
	}
	
	/**
	 * This weapon will always be a gun
	 * @return weapon number 1 or null
	 */
	public Weapon getWeapon1(){
		return wep1;
	}
	
	/**
	 * This weapon will always be a cannon
	 * @return weapon number 2 or null
	 */
	public Weapon getWeapon2(){
		return wep2;
	}
	

	/**
	 * calculates the damage dealt (does not actually deal the damage)
	 * @param a weapon
	 * @param a Unit
	 * @return the damage that would be dealt
	 */
	public int getDamage(Weapon weapon, Unit a){
		if (weapon instanceof Cannon)
			return (int) (weapon.getBasedam() *  Overlord.getCommand().getAttackMod(this, a) * a.getHQ().getCommand().getDefenseMod(a, this) */* terrain.defenceMultiplier() * */a.getCResis() * getBonus() * weapon.getWeaponBonus(a) * health / 100);
		else
			return (int) (weapon.getBasedam() * Overlord.getCommand().getAttackMod(this, a) * a.getHQ().getCommand().getDefenseMod(a, this) */* terrain.defenceMultiplier() * */ a.getGResis() * getBonus() * weapon.getWeaponBonus(a) * health / 100);

	}
	

	
	/**
	 * gives a description of the unit
	 * it is implemented in each specific unit class
	 */
	public abstract String toString(); //used for unit description

}






